package cn.comtom.ewbs.signature.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import com.google.common.collect.Maps;

import cn.comtom.ewbs.signature.common.utils.Base64Util;
import cn.comtom.ewbs.signature.common.utils.ConfigFileUtil;
import cn.comtom.ewbs.signature.common.vo.request.CertApplyReq;
import cn.comtom.ewbs.signature.common.vo.request.Sm2Req;
import cn.comtom.ewbs.signature.common.vo.request.SoftalgCertImportReq;
import cn.comtom.ewbs.signature.common.vo.response.CertResp;
import cn.comtom.ewbs.signature.service.AbstractSignService;
import cn.comtom.ewbs.signature.service.Sm2Service;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.asymmetric.SM2;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/*1）@CacheConfig(cacheNames = {“lemonCache”})设置了ehcache的名称，这个名称就是ehcache.xml内的名称；
 2）@Cacheable：应用到读取数据的方法上，即可缓存的方法，如查找方法：先从缓存中读取，如果没有再调 用方法获取数据，然后把数据添加到缓存中，适用于查找；
3）@CachePut：主要针对方法配置，能够根据方法的请求参数对其结果进行缓存，和 @Cacheable 不同的是，它每次都会触发真实方法的调用。适用于更新和插入；
4）@CacheEvict：主要针对方法配置，能够根据一定的条件对缓存进行清空。适用于删除。*/
@Slf4j
@Service("softalg")
@Scope("singleton")
@Data
@ConfigurationProperties(prefix = "sign.softalg")
public class SoftAlgServiceImpl extends AbstractSignService implements InitializingBean, DisposableBean {

    private String certNo;

    private String priviteKey;

    private String publicKey;

    public Map<String,String> trustCertMap;
    
    @Autowired
    private Sm2Service sm2Service;

    @Override
    public String getByteSign(byte[] data) throws Exception {
        String signHex= getSignForByte(data);
        log.info("getByteSign-data:{},sign:{}", ArrayUtil.toString(data), signHex);
        //utc+certNo
        String utc = UTC();
        signHex = utc.concat(certNo).concat(signHex);
        return signHex;
    }

    @Override
    public Boolean verifyByteSign(byte[] data, String signValue) throws Exception {
        String signHexStr = signValue.substring(20);
        return verfySignForByte(data,signHexStr,getPublicKeyP(signValue));
    }
    
    public String getSm2ByteSign(byte[] data) throws Exception {
    	String encodeHexStr = HexUtil.encodeHexStr(data);
    	String base64Data = Base64Utils.encodeToString(encodeHexStr.getBytes());
    	//String base64Data = Base64Utils.encodeToString(data);
    	Sm2Req sm2Req = new Sm2Req(base64Data);
    	sm2Req.setPrivite_key(priviteKey);
    	sm2Req.setPublic_key(publicKey);
    	sm2Req.setId(certNo);
    	String base64Sign = sm2Service.getByteSign(sm2Req);
    	byte[] byteSign = Base64Utils.decodeFromString(base64Sign);
        String signHex = HexUtil.encodeHexStr(byteSign);
        //utc+certNo
        String utc = UTC();
        signHex = utc.concat(certNo).concat(signHex);
        return signHex;
    }

    public Boolean verifySm2ByteSign(byte[] data, String signValue) throws Exception {
    	String encodeHexStr = HexUtil.encodeHexStr(data);
    	String base64Data = Base64Utils.encodeToString(encodeHexStr.getBytes());
    	//String base64Data = Base64Utils.encodeToString(data);
    	String signHexStr = signValue.substring(20);
    	String base64Sign = Base64Utils.encodeToString(HexUtil.decodeHex(signHexStr));
    	Sm2Req sm2Req = new Sm2Req(base64Data,base64Sign);
    	sm2Req.setPublic_key(getPublicKeyP(signValue));
    	return sm2Service.verifyByteSign(sm2Req);
    }

    @Override
    public String getFileSign(byte[] data) throws Exception {
        String dataHex = HexUtil.encodeHexStr(data);
        String signHex= getSign(dataHex);
        //utc+certNo
        String utc = UTC();
        signHex = utc.concat(certNo).concat(signHex);
        byte[] byteSign = HexUtil.decodeHex(signHex);
        String base64sign = Base64Utils.encodeToString(byteSign);
        log.info("getByteSign.base64Data:{},",base64sign);
        return base64sign;
    }

    @Override
    public Boolean verifyFileSign(byte[] data, String base64SignValue) throws Exception {
        String dataHex = HexUtil.encodeHexStr(data);
        byte[] signbytes = Base64Utils.decodeFromString(base64SignValue);
        String signHex = HexUtil.encodeHexStr(signbytes);
        String signHexStr = signHex.substring(20);
        return this.verfySign(dataHex,signHexStr,getPublicKeyP(signHex));
    }

    @Override
    public CertResp certApply(CertApplyReq certApplyReq) throws Exception {
        return null;
    }

    @Override
    public boolean openDevice() {
        log.info("本级证书:{}",certNo);
        log.info("信任证书:{}",trustCertMap.keySet());
        return true;
    }

    @Override
    public void closeDevice() {

    }

    @Override
    public Boolean importCert(List<String> certs) {
        return null;
    }

    /**
     * 导入信任证书
     * @param certReq
     * @return
     */
    public Boolean importTrustCert(SoftalgCertImportReq certReq) {
        if(trustCertMap == null){
            trustCertMap = Maps.newHashMap();
        }
        trustCertMap.put(certReq.getCertNo(),certReq.getPublicKey());
        log.info("信任证书:{}",trustCertMap.keySet());
        return true;
    }

    @Override
    public Boolean importCertauthList(String certauthList) {
        return null;
    }

    @Override
    public void destroy() throws Exception {

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        openDevice();
    }

    /**
     * 当前时间转换为16进制
     * @return
     */
    public static String UTC(){
        Date date = new Date();
        Long ab = date.getTime()/1000;
        String a = Long.toHexString(ab);
        return a.toUpperCase();
    }

    /**
     * 从签名值中获取证书号
     * @param signValue
     * @return
     */
    private String getPublicKeyCertNoP(String signValue){
        return signValue.substring(8, 20);
    }

    /**
     * 获取验签的公钥
     * @param signValue
     * @return
     */
    private String getPublicKeyP(String signValue){
        String certNo = getPublicKeyCertNoP(signValue);
        String publicKey = trustCertMap.get(certNo);
        if(StringUtils.isBlank(publicKey)){
            log.error("信任证书：{} 不存在",certNo);
            return null;
        }
        return publicKey;
    }

    /**
     * 验证签名
     * @param dataHex
     * @param signHex
     * @param publicKeyP
     * @return
     */
    private boolean verfySign(String dataHex, String signHex, String publicKeyP){
        String publicKeyX = publicKeyP.substring(0,64);
        String publicKeyY = publicKeyP.substring(64);
        final SM2 sm2 = new SM2(priviteKey, publicKeyX, publicKeyY);
        sm2.usePlainEncoding();
        return sm2.verifyHex(dataHex, signHex);
    }

    /**
     * 获取签名
     * @param dataHex
     * @return
     */
    private String getSign(String dataHex){
        String publicKeyX = publicKey.substring(0,64);
        String publicKeyY = publicKey.substring(64);
        final SM2 sm2 = new SM2(priviteKey, publicKeyX, publicKeyY);
        sm2.usePlainEncoding();
        return sm2.signHex(dataHex);
    }
    
    /**
     * 获取指令数据签名
     * @param dataHex
     * @return
     */
    private String getSignForByte(byte[] dataByte){
        String publicKeyX = publicKey.substring(0,64);
        String publicKeyY = publicKey.substring(64);
        final SM2 sm2 = new SM2(priviteKey, publicKeyX, publicKeyY);
        sm2.usePlainEncoding();
        byte[] sign = sm2.sign(dataByte);
        String signHex = HexUtil.encodeHexStr(sign);
        return signHex.toUpperCase();
    }
    
    /**
     * 验证指令数据签名
     * @param dataHex
     * @param signHex
     * @param publicKeyP
     * @return
     */
    private boolean verfySignForByte(byte[] dataByte, String signHex, String publicKeyP){
        String publicKeyX = publicKeyP.substring(0,64);
        String publicKeyY = publicKeyP.substring(64);
        final SM2 sm2 = new SM2(priviteKey, publicKeyX, publicKeyY);
        sm2.usePlainEncoding();
        byte[] signByte = HexUtil.decodeHex(signHex.toLowerCase());
        return sm2.verify(dataByte, signByte);
    }
    
	@Override
	public boolean updateConfig(String configFilePath, String ip, Integer port, String signNo, String logPath, Integer conLimit, Integer timeOut) {
		return true;
	}

    public static void main(String[] args) {
    	byte[] dd = new byte[] {73, 0, 1, 6, 1, 0, 0, 0, 10, 80, 2, 1, 6, 2, 17, 34, 118, 0, 0};
        String sign="YdfdFAAAAAAAAc1zIw4X+JzQ0jLOkjPAAmyqAWZwVjF1tBrm47w++gdGip5tF3YhB+RG5awLrXWt1XIuM4+cccbbwYqJKKGaOio=";
        byte[] bytes1 = Base64Utils.decodeFromString(sign);
        String encodeHexStr = HexUtil.encodeHexStr(bytes1);
        Console.log(encodeHexStr);
        String encodeToString = Base64Utils.encodeToString(HexUtil.decodeHex(encodeHexStr));
        Console.log(encodeToString);
        Console.log(sign.substring(8,20));
        byte[] x = new byte[]{12,3,45,23,1};
        Console.log(Base64Utils.encodeToString(x));
        Console.log(Base64Util.byte2ToBase64(x));
        byte[] bytes = Base64Utils.decodeFromString("DAMtFwE=");
    }
}
